using System;
using System.Data;
using System.Text;
using System.Text.RegularExpressions;
using EventHandler.Libraries.Business.Database;
using EventHandler.Libraries.Business.Security.Authentication;

namespace EventHandler.Libraries.Business.Security
{
    public class AccountHelper : IAccountHelper
    {
        private readonly IDatabaseHelper _dbHelper;

        public AccountHelper(IDatabaseHelper db)
        {
            _dbHelper = db;
        }

		public IAccount CreateAccount(IAccount account)
		{
			if (account == null)
			{
				throw new ArgumentNullException("account");
			}

			Validate(account);
			int? newAccountId = CreateUserAccount(account);
			if (newAccountId.HasValue && newAccountId.Value >= 0)
			{
				account.AccountId = newAccountId.Value;
			}	
			else
			{
				throw new ApplicationException("Unable to create a new account");
			}

			return account;
		}

		public void DeleteAccount(int accountId)
		{
			using (var cn = _dbHelper.GetConnection())
			{
				cn.Open();

				var cmd = cn.CreateCommand();
				cmd.CommandType = CommandType.StoredProcedure;
				cmd.CommandText = "Auth.DeleteAccount";
				AddParameter(cmd, "@AccountID", accountId, null, DbType.Int32);
				
				cmd.ExecuteNonQuery();

				cn.Close();
			}
		}

		public IAccount RetrieveAccount(string userName)
		{
			if (string.IsNullOrEmpty(userName))
			{
				throw new ArgumentException("Username is required.");
			}

			IAccount results = null;

			return results;
		}

        private void Validate(IAccount account)
        {
            StringBuilder errors = new StringBuilder();
            if (string.IsNullOrEmpty(account.FirstName))
            {
                errors.Append("First Name is required\n");
            }
            if (string.IsNullOrEmpty(account.Email))
            {
                errors.Append("Email is required\n");
            }
            if (string.IsNullOrEmpty(account.LastName))
            {
                errors.Append("Last Name is required\n");
            }
            if (string.IsNullOrEmpty(account.Password))
            {
                errors.Append("Password is required\n");
            }
            if (string.IsNullOrEmpty(account.Username))
            {
                errors.Append("Username is required\n");
            }
            if(UsernameAlreadyExists(account.Username))
            {
                errors.Append("Username already exists\n");
            }
            if(!PasswordStrengthAdequate(account.Password))
            {
                errors.Append("Password strength isn't adequate");
            }
            if(errors.Length>0)
            {
                throw new ArgumentException(errors.ToString());
            }
        }

        public bool PasswordStrengthAdequate(string password)
        {
			if (password == null)
			{
				return false;
			}

            //^\w* => must start with an alphanumeric character
            //(?=\w*\d) => must contain at least one numeric digit
            //(?=\w*[a-z]) => must contain at least one alphabetic character
            //\w*$ => must end in an alphanumeric character
            var numberAndLetter = new Regex(@"^\w*(?=\w*\d)(?=\w*[a-z])\w*$");
            return password.Length >= 6 && numberAndLetter.IsMatch(password);
        }


        private bool UsernameAlreadyExists(string username)
        {
			var result = true;

			using (var cn = _dbHelper.GetConnection())
			{
				cn.Open();

				var cmd = cn.CreateCommand();
				cmd.CommandType = CommandType.StoredProcedure;
				cmd.CommandText = "Auth.GetUserByUserName";
				AddParameter(cmd, "@UserName", username, 255, DbType.String);

				using (var dr = cmd.ExecuteReader())
				{
					if (dr.Read())
					{
						result = true;
					}
					else {
						result = false;
					}
				}

				cn.Close();
			}

			return result;
        }

		private int? CreateUserAccount(IAccount account)
		{
			int? accountId = null;

			using (var cn = _dbHelper.GetConnection())
			{
				cn.Open();

				var cmd = cn.CreateCommand();
				cmd.CommandType = CommandType.StoredProcedure;
				cmd.CommandText = "Auth.CreateNewAccount";
				AddParameter(cmd, "@FirstName", account.FirstName, 30, DbType.String);
				AddParameter(cmd, "@LastName", account.LastName, 30, DbType.String);
                AddParameter(cmd, "@EmailAddress", account.Email, 255, DbType.String);
				AddParameter(cmd, "@Password", account.Password, 255, DbType.String);
				AddParameter(cmd, "@AuthenticationSignInName", account.Username, 255, DbType.String);
				AddParameter(cmd, "@AuthenticationTypeID", account.AuthenticationTypeId, null, DbType.Int16);

				var accountIdOutputParameter = cmd.CreateParameter();
				accountIdOutputParameter.ParameterName = "@AccountID";
				accountIdOutputParameter.DbType = DbType.Int32;
				accountIdOutputParameter.Direction = ParameterDirection.Output;
				cmd.Parameters.Add(accountIdOutputParameter);

				cmd.ExecuteNonQuery();

				accountId = (int)accountIdOutputParameter.Value;

				cn.Close();
			}

			return accountId;
		}


		private void AddParameter(IDbCommand cmd, string parameterName, object value, int? size, DbType type)
		{
			IDbDataParameter parameter = cmd.CreateParameter();
			
			if (size.HasValue)
			{
				parameter.Size = size.Value;
			}
			
			parameter.ParameterName = parameterName;
			parameter.Value = value;
			parameter.DbType = type;

			cmd.Parameters.Add(parameter);
		}

    }
}